﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace QS.Tool.Common.IDCard
{
    public class IDCardHelper
    {
        #region 身份证算法说明
        //        C#实现的18位身份证格式验证算法 

        //18位身份证标准在国家质量技术监督局于1999年7月1日实施的gb11643-1999《公民身份号码》中做了明确的规定。 
        //gb11643-1999《公民身份号码》为gb11643-1989《社会保障号码》的修订版，其中指出将原标准名称"社会保障号
        //码"更名为"公民身份  号码"，另外gb11643-1999《公民身份号码》从实施之日起代替gb11643-1989。
        //gb11643-1999《公民身份号码》主要内容如  下：  
        //一、范围  
        //该标准规定了公民身份号码的编码对象、号码的结构和表现形式，使每个编码对象获得一个唯一的、
        //不变的法定号码。 
        // 二、编码对象  
        //公民身份号码的编码对象是具有中华人民共和国国籍的公民。  
        //三、号码的结构和表示形式  
        //1、号码的结构  
        //公民身份号码是特征组合码，由十七位数字本体码和一位校验码组成。排列顺序从左至右依次
        //为：
        //六位数字地址码，八位数字出生日期码，三位数字顺序码和一位数字校验码。  
        //2、地址码  
        //表示编码对象常住户口所在县(市、旗、区)的行政区划代码，按gb/t2260的规定执行。 
        //3、出生日期码  
        //表示编码对象出生的年、月、日，按gb/t7408的规定执行，年、月、日代码之间不用分隔符。  

        //4、顺序码  
        //表示在同一地址码所标识的区域范围内，对同年、同月、同日出生的人编定的顺序号，顺序码的奇数分配给男性，偶数分配给女性。  
        //5、校验码  

        //（1）十七位数字本体码加权求和公式  
        //s = sum(ai * wi), i = 0, ... , 16 ，先对前17位数字的权求和  
        //ai:表示第i位置上的身份证号码数字值  
        //wi:表示第i位置上的加权因子  
        //wi: 7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2 
        //（2）计算模  y = mod(s, 11)（就是用求得的和除以11得到余数）  

        //（3）通过模得到对应的校验码
        //y: 0 1 2 3 4 5 6 7 8 9 10 （余数） 
        //校验码: 1 0 x 9 8 7 6 5 4 3 2  
        //四、举例如下：  

        //北京市朝阳区: 11010519491231002x  
        //广东省汕头市: 440524188001010014  
        #endregion

        public static bool CheckIDCard(string idCard)
        {
            //正则验证是否合法
            Regex regex = new Regex(@"(^\d{18}$)|(^\d{17}(X|x)$)");
            if (!regex.IsMatch(idCard))
            {
                return false;
            }
            //验证前两位是否符合地区代码
            string[] area = { "11", "12", "13", "14", "15", "21", "22", "23", "31", "32", "33", "34", "35", "36", "37", "41", "42", "43", "44", "45", "46", "50", "51", "52", "53", "54", "61", "62", "63", "64", "65", "71", "81", "82", "91" };
            string num2 = idCard.Substring(0, 2);
            if (!area.Contains(num2))
            {
                return false;
            }
            //验证出生日期
            string birthday = idCard.Substring(6, 4) + "/" + idCard.Substring(10, 2) + "/" + idCard.Substring(12, 2);
            DateTime dateTime = DateTime.Now;
            if (!DateTime.TryParse(birthday, out dateTime))
            {
                return false;
            }
            DateTime maxBirthday = DateTime.Now.Date;
            DateTime minBirthday = maxBirthday.AddYears(-200);
            if (dateTime>maxBirthday||dateTime<minBirthday)
            {
                return false;
            }
            //验证最后一位校验位是否符合规则
            int[] wQuan = { 7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2 };
            string checkWei = "10x98765432";
            string num17 = idCard.Substring(0, 17);
            string num18 = idCard.Substring(17);
            int sum = 0;
            for (int i = 0; i < 17; i++)
            {
                sum += Convert.ToInt32(num17[i].ToString()) * wQuan[i];
            }
            int mod = sum % 11;
            string result = checkWei[mod].ToString();
            if (num18.Equals(result,StringComparison.OrdinalIgnoreCase))
            {
                return true;
            }
            return false;
        }
    }
}
